package main

import (
	"fmt"
	"net/http"
	"project3/model"
	"project3/mysql"
	"time"

	"github.com/gin-gonic/gin"
	_ "github.com/go-sql-driver/mysql"
	"github.com/golang-jwt/jwt/v5"
	"golang.org/x/crypto/bcrypt"
	//"gorm.io/gorm/logger"
)

var jwtSecret = []byte("wangyachuan")

func Register(ctx *gin.Context) {

	db := mysql.GetDB()
	if db == nil {
		ctx.JSON(500, gin.H{
			"code":    500,
			"message": "数据库连接失败",
		})
		return
	}

	var requestUser model.User
	if err := ctx.Bind(&requestUser); err != nil {
		ctx.JSON(http.StatusUnprocessableEntity, gin.H{
			"code":    422,
			"message": "参数绑定失败: " + err.Error(),
		})
		return
	}

	name := requestUser.Name
	telephone := requestUser.Telephone
	password := requestUser.Password

	if len(name) == 0 {
		ctx.JSON(422, gin.H{
			"code":    422,
			"message": "用户名不能为空",
		})
		return
	}

	if len(telephone) != 11 {
		ctx.JSON(http.StatusUnprocessableEntity, gin.H{
			"code":    422,
			"message": "手机号必须为11位",
		})
		return
	}

	if len(password) < 6 {
		ctx.JSON(http.StatusUnprocessableEntity, gin.H{
			"code":    422,
			"message": "密码不能少于6位",
		})
		return
	}

	var user model.User
	db.Where("telephone = ?", telephone).First(&user)
	if user.ID != 0 {
		// 如果手机号已存在，则返回422无法处理的实体错误
		ctx.JSON(http.StatusUnprocessableEntity, gin.H{
			"code":    422,
			"message": "用户已存在",
		})
		return
	}

	// 对密码进行加密处理
	hasedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
	if err != nil {
		// 如果密码加密失败，则返回500内部服务器错误
		ctx.JSON(http.StatusUnprocessableEntity, gin.H{
			"code":    500,
			"message": "密码加密错误",
		})
		return
	}

	// 创建新用户
	newUser := model.User{
		Name:      name,
		Telephone: telephone,
		Password:  string(hasedPassword),
	}
	db.Create(&newUser)

	// 注册成功，返回200成功状态码和成功消息
	ctx.JSON(http.StatusOK, gin.H{
		"code":    200,
		"message": "注册成功",
	})
}

func Login(ctx *gin.Context) {
	db := mysql.GetDB()
	var requestUser model.User
	if err := ctx.Bind(&requestUser); err != nil {
		return
	}
	telephone := requestUser.Telephone
	password := requestUser.Password
	if len(telephone) != 11 {
		ctx.JSON(http.StatusUnprocessableEntity, gin.H{
			"code":    422,
			"message": "手机号必须为11位",
		})
		return
	}
	if len(password) < 6 {
		ctx.JSON(http.StatusUnprocessableEntity, gin.H{
			"code":    422,
			"message": "密码不能少于6位",
		})
		return
	}
	var user model.User
	db.Where("telephone = ?", telephone).First(&user)
	if user.ID == 0 {
		ctx.JSON(http.StatusUnprocessableEntity, gin.H{
			"code":    422,
			"message": "用户不存在",
		})
		return
	}
	if err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password)); err != nil {
		ctx.JSON(http.StatusUnprocessableEntity, gin.H{
			"code":    422,
			"message": "密码错误",
		})
		return
	}
	claims := &jwt.RegisteredClaims{
		ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 24)),
		Issuer:    "wangyachaun",
		Subject:   fmt.Sprintf("%d", user.ID),
	}
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	tokenString, err := token.SignedString(jwtSecret)
	if err != nil {
		ctx.JSON(http.StatusInternalServerError, gin.H{
			"code":    500,
			"message": "服务器内部错误",
		})
		return
	}
	ctx.JSON(http.StatusOK, gin.H{
		"code":    200,
		"message": "登录成功",
		"token":   tokenString,
	})
}

// CustomClaims 是一个自定义的JWT声明结构体，它嵌入了jwt.RegisteredClaims结构体
// 并添加了UID和Username两个字段，用于存储用户相关的额外信息
type CustomClaims struct {
	jwt.RegisteredClaims        // 嵌入jwt.RegisteredClaims，获得标准的JWT声明字段
	Telephone            uint   `json:"uid"`      // 用户的唯一ID
	Name                 string `json:"username"` // 用户名

}

// ParseAndVerifyJWT 函数用于解析并验证JWT令牌
// 参数tokenString是待解析的JWT字符串
// 返回值是一个指向CustomClaims的指针和一个error，如果解析或验证失败，error不为nil
func ParseAndVerifyJWT(tokenString string) (*CustomClaims, error) {
	// 使用jwt.ParseWithClaims函数解析JWT，并传入CustomClaims作为预期的声明类型
	// 第二个参数是一个函数，用于提供密钥来验证JWT的签名
	token, err := jwt.ParseWithClaims(tokenString, &CustomClaims{}, func(token *jwt.Token) (interface{}, error) {
		// 这里直接返回了jwtSecret作为密钥，jwtSecret应该是一个在程序其他地方定义的变量
		// 假设jwtSecret是一个有效的密钥，用于验证JWT的签名
		return jwtSecret, nil
	})

	if err != nil {
		// 如果解析失败，返回nil和错误
		return nil, err
	}

	// 检查token的Claims是否可以被转换为*CustomClaims，并且token是否有效
	if claims, ok := token.Claims.(*CustomClaims); ok && token.Valid {
		// 如果转换成功且token有效，返回claims和nil
		return claims, nil
	}

	// 如果以上条件都不满足，返回nil和一个表示"无效token"的错误
	return nil, fmt.Errorf("无效 token")
}

// AuthMiddleware 函数返回一个gin框架的中间件函数
// 这个中间件用于验证请求的JWT令牌
func AuthMiddleware() gin.HandlerFunc {
	return func(c *gin.Context) {
		// 从请求头中获取Authorization字段的值
		tokenString := c.GetHeader("Authorization")
		c.GetHeader("Header-Name")
		//fmt.Println(c.GetHeader(""))

		// 如果存在并且以"Bearer "开头，则截取"Bearer "之后的部分作为JWT令牌
		if len(tokenString) > 7 && tokenString[:7] == "Bearer " {
			tokenString = tokenString[7:]
		} else {
			// 如果不是以"Bearer "开头或者长度不够，则将tokenString置为空字符串
			tokenString = ""
		}

		// 打印当前的token字符串，用于调试
		fmt.Println(tokenString, "当前token")

		// 如果token字符串为空，则返回未经授权的响应并终止请求
		if tokenString == "" {
			c.JSON(http.StatusUnauthorized, gin.H{
				"code":    1005,
				"message": "必须传递token",
			})
			c.Abort() // 终止请求
			return
		}

		// 调用ParseAndVerifyJWT函数解析并验证JWT令牌
		claims, err := ParseAndVerifyJWT(tokenString)
		if err != nil {
			c.JSON(http.StatusUnauthorized, gin.H{
				"code":    1005,
				"message": "token解析错误",
			})
			c.Abort() // 终止请求
			return
		}

		// 将解析出的用户ID和用户名存储到gin.Context中，供后续处理使用
		c.Set("Telephone", claims.Telephone)
		c.Set("Name", claims.Name)

		// 继续处理请求
		c.Next()
	}
}

func main() {
	// 创建一个 Gin 引擎实例
	r := gin.Default()
	// 只为特定的路由组注册中间件
	protected := r.Group("/")
	protected.Use(AuthMiddleware()) // 这里注册中间件
	{
		protected.GET("/ping", func(c *gin.Context) {
			Telephone := c.MustGet("Telephone").(uint)
			Name := c.MustGet("Name").(string)
			fmt.Printf("Telephone: %d,Name: %s\n", Telephone, Name)
			c.JSON(http.StatusOK, gin.H{
				"message": "Protected ping",
			})
		})
		// 可以添加更多需要保护的路由
	}

	r.Run(":8080") // 启动服务器
}
